{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ORCL vs NVDA\n",
      "Last PnL: 0.0\n",
      "No trades\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f3985952e10>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AAPL vs ORCL\n",
      "Last PnL: 0.0\n",
      "No trades\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f398a925810>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NVDA vs MSFT\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "##The parameters are the same as in the other file, BUT threshold is removed, and instead we have to choose constans rho>0 (urgency parameter) and c\n",
    "## and c>0 (a measure of the transaction costs). I have incorportared these two parameters to the class constructor and removed thresholds\n",
    "## rho should be close to 0 (I have tried 0.01 and 0.001) and c as well (I have tried 0.01)\n",
    "\n",
    "##The only differences with the other file are the block with all the new functions to compute the thresholds, and then in the execution part\n",
    "#that instead of comparing s with fixed thresholds, we compare the last residual with a threshold that deppends on the OU parameters\n",
    "\n",
    "\n",
    "import sys\n",
    "from simulator import (\n",
    "    Simulator, string_to_micro, micro_to_time,\n",
    "    BUY, SELL, SHORT, EXCH_INET,\n",
    "    BOOK_DEPTH1_PRICE, ORDER_EVENTS,\n",
    "    )\n",
    "import numpy as np\n",
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "from sklearn.linear_model import LinearRegression\n",
    "from statsmodels.tsa.ar_model import AR\n",
    "from scipy import integrate \n",
    "from scipy import optimize\n",
    "\n",
    "\n",
    "#### Functions to compute thresholds\n",
    "def fplus(u,rho,epsilon,kappa, theta, sigma):\n",
    "    #return u**(rho/kappa-1)*np.exp(-np.sqrt(2*kappa/sigma**2)*(theta-epsilon)*u-u**2/2)\n",
    "    return sigma**(rho/kappa)*u**(rho/kappa-1)*np.exp(-np.sqrt(2*kappa)*(theta-epsilon)*u-(u*sigma)**2/2)\n",
    "\n",
    "\n",
    "def fplus_der(u,rho,epsilon,kappa, theta, sigma):\n",
    "    #return np.sqrt(2*kappa/sigma**2)*u**(rho/kappa)*np.exp(-np.sqrt(2*kappa/sigma**2)*(theta-epsilon)*u-u**2/2)\n",
    "    return sigma**(rho/kappa)*np.sqrt(2*kappa)*u**(rho/kappa)*np.exp(-np.sqrt(2*kappa)*(theta-epsilon)*u-(u*sigma)**2/2)\n",
    "\n",
    "\n",
    "def fminus(u,rho,epsilon,kappa, theta, sigma):\n",
    "    #return u**(rho/kappa-1)*np.exp(np.sqrt(2*kappa/sigma**2)*(theta-epsilon)*u-u**2/2)\n",
    "    return sigma**(rho/kappa)*u**(rho/kappa-1)*np.exp(np.sqrt(2*kappa)*(theta-epsilon)*u-(u*sigma)**2/2)\n",
    "\n",
    "\n",
    "def fminus_der(u,rho,epsilon,kappa, theta, sigma):\n",
    "    #return -np.sqrt(2*kappa/sigma**2)*u**(rho/kappa)*np.exp(np.sqrt(2*kappa/sigma**2)*(theta-epsilon)*u-u**2/2)\n",
    "    return -sigma**(rho/kappa)*np.sqrt(2*kappa)*u**(rho/kappa)*np.exp(-np.sqrt(2*kappa)*(theta-epsilon)*u-(u*sigma)**2/2)\n",
    "\n",
    "\n",
    "def Fplus(epsilon,rho, kappa, theta, sigma):\n",
    "    integral,error = integrate.quad(fplus,0, np.inf, args = (rho,epsilon,kappa,theta,sigma,))\n",
    "    return integral\n",
    "\n",
    "\n",
    "def Fminus(epsilon,rho, kappa, theta, sigma):\n",
    "    integral,error = integrate.quad(fminus,0, np.inf, args = (rho,epsilon,kappa,theta,sigma,))\n",
    "    return integral\n",
    "\n",
    "\n",
    "def Fplus_der(epsilon,rho, kappa, theta, sigma):\n",
    "    integral,error = integrate.quad(fplus_der,0, np.inf, args = (rho,epsilon,kappa,theta,sigma,))\n",
    "    return integral\n",
    "\n",
    "\n",
    "def Fminus_der(epsilon,rho, kappa, theta, sigma):\n",
    "    integral,error = integrate.quad(fminus_der,0, np.inf, args = (rho,epsilon,kappa,theta,sigma,))\n",
    "    return integral\n",
    "\n",
    "\n",
    "def long_close_function(epsilon,rho, kappa, theta, sigma,c):\n",
    "    return (epsilon[0] - c)*Fplus_der(epsilon[0],rho, kappa, theta, sigma)-Fplus(epsilon[0],rho, kappa, theta, sigma)\n",
    "\n",
    "\n",
    "def long_close(rho, kappa, theta, sigma, c): #epsilon^*+\n",
    "    result = optimize.root(long_close_function,[1], args=(rho, kappa, theta, sigma, c,), method = 'lm')\n",
    "    if result.success:\n",
    "        return result.x[0]\n",
    "    else:\n",
    "        return (kappa*theta+c*rho)/(rho+kappa)\n",
    "\n",
    "    \n",
    "def short_close_function(epsilon,rho, kappa, theta, sigma,c):\n",
    "    return (epsilon[0] + c)*Fminus_der(epsilon[0],rho, kappa, theta, sigma)-Fminus(epsilon[0],rho, kappa, theta, sigma)\n",
    "\n",
    "\n",
    "def short_close(rho, kappa, theta, sigma, c): #epsilon^*-\n",
    "    result = optimize.root(short_close_function,[-1], args=(rho, kappa, theta, sigma, c,), method = 'lm')\n",
    "    if result.success:\n",
    "        return result.x[0]\n",
    "    else:\n",
    "        return (kappa*theta-c*rho)/(rho+kappa)\n",
    "\n",
    "    \n",
    "def Hplus(epsilon,kappa,theta, sigma,rho,c):\n",
    "    epsilonplus = long_close(rho, kappa, theta, sigma,c)\n",
    "    if epsilon >= epsilonplus:\n",
    "        return epsilon - c\n",
    "    else:\n",
    "        return (epsilonplus - c)*Fplus(epsilon,rho, kappa, theta, sigma)/Fplus(epsilonplus,rho, kappa, theta, sigma)\n",
    "    \n",
    "    \n",
    "def Hplus_der(epsilon,kappa,theta, sigma,rho,c):\n",
    "    epsilonplus = long_close(rho, kappa, theta, sigma,c)\n",
    "    if epsilon >= epsilonplus:\n",
    "        return 1\n",
    "    else:\n",
    "        return (epsilonplus - c)*Fplus_der(epsilon,rho, kappa, theta, sigma)/Fplus(epsilonplus,rho, kappa, theta, sigma)\n",
    "\n",
    "    \n",
    "def Hminus(epsilon,kappa,theta, sigma,rho, c):\n",
    "    epsilonminus = short_close(rho, kappa, theta, sigma,c)\n",
    "    if epsilon <= epsilonminus:\n",
    "        return -epsilon - c\n",
    "    else:\n",
    "        return -(epsilonminus + c)*Fminus(epsilon,rho, kappa, theta, sigma)/Fminus(epsilonminus,rho, kappa, theta, sigma)\n",
    "    \n",
    "    \n",
    "def Hminus_der(epsilon,kappa,theta, sigma,rho, c):\n",
    "    epsilonminus = short_close(rho, kappa, theta, sigma,c)\n",
    "    if epsilon <= epsilonminus:\n",
    "        return -1\n",
    "    else:\n",
    "        return -(epsilonminus + c)*Fminus_der(epsilon,rho, kappa, theta, sigma)/Fminus(epsilonminus,rho, kappa, theta, sigma)\n",
    "\n",
    "    \n",
    "def long_short_open_function(epsilon,rho,kappa,theta,sigma, c):\n",
    "    minusepsilon = epsilon[0]\n",
    "    plusepsilon = epsilon[1]\n",
    "    \n",
    "    numA = Fminus(minusepsilon,rho, kappa, theta, sigma)*(Hplus(plusepsilon,kappa,theta,sigma,rho,c)-plusepsilon-c)-Fminus(plusepsilon,rho, kappa, theta, sigma)*(Hminus(minusepsilon,kappa,theta,sigma,rho,c)+minusepsilon-c)\n",
    "    denA = Fplus(plusepsilon,rho, kappa, theta, sigma)*Fminus(minusepsilon,rho, kappa, theta, sigma)-Fplus(minusepsilon,rho, kappa, theta, sigma)*Fminus(plusepsilon,rho, kappa, theta, sigma)\n",
    "    A = numA/denA\n",
    "\n",
    "    numB = Fplus(minusepsilon,rho, kappa, theta, sigma)*(Hplus(plusepsilon,kappa,theta,sigma,rho,c)-plusepsilon-c)-Fplus(plusepsilon,rho, kappa, theta, sigma)*(Hminus(minusepsilon,kappa,theta,sigma,rho,c)+minusepsilon-c)\n",
    "    denB = Fminus(plusepsilon,rho, kappa, theta, sigma)*Fplus(minusepsilon,rho, kappa, theta, sigma)-Fminus(minusepsilon,rho, kappa, theta, sigma)*Fplus(plusepsilon,rho, kappa, theta, sigma)\n",
    "    B = numB/denB\n",
    "    \n",
    "    y_0 = A*Fplus_der(plusepsilon,rho, kappa, theta, sigma)+B*Fminus_der(plusepsilon,rho, kappa, theta, sigma)+1-Hplus_der(plusepsilon,kappa,theta, sigma,rho, c)\n",
    "    y_1 = A*Fplus_der(minusepsilon,rho, kappa, theta, sigma)+B*Fminus_der(minusepsilon,rho, kappa, theta, sigma)-1-Hminus_der(minusepsilon,kappa,theta, sigma,rho, c)\n",
    "    \n",
    "    return [y_0,y_1]\n",
    "\n",
    "\n",
    "def long_short_open(rho,kappa,theta,sigma, c):\n",
    "    return optimize.root(long_short_open_function,[-0.18,1.5 ], args=(rho, kappa, theta, sigma, c,), method = 'hybr').x\n",
    "\n",
    "\n",
    "def long_open(rho,kappa,theta,sigma, c):\n",
    "    return short_close(rho,kappa,theta,sigma, c)\n",
    "\n",
    "\n",
    "def short_open(rho,kappa,theta,sigma, c):\n",
    "    return long_close(rho,kappa,theta,sigma, c)\n",
    "\n",
    "\n",
    "def regress(returns1,returns2):\n",
    "    x = np.asarray(returns1).reshape(-1,1)\n",
    "    y = np.asarray(returns2).reshape(-1,1)\n",
    "    model = LinearRegression()\n",
    "    model.fit(x,y)\n",
    "    a = model.intercept_[0]\n",
    "    b = model.coef_[0,0]\n",
    "    residuals = y-model.predict(x)\n",
    "    return residuals, a,b\n",
    "\n",
    "\n",
    "def returns(midprices):\n",
    "    log_return = np.diff(np.log(midprices), axis=-1)\n",
    "    return log_return\n",
    "\n",
    "\n",
    "def fitOU(residual, training_size):\n",
    "    dt = 1\n",
    "    ou = np.cumsum(residual)\n",
    "    model = AR(ou)\n",
    "    fittedmodel = model.fit(maxlag=1, disp=-1)  \n",
    "    a = fittedmodel.params[0]\n",
    "    b = fittedmodel.params[1]\n",
    "    var =  fittedmodel.sigma2\n",
    "    if b > 0.0 and b < np.exp(-2.0/training_size):\n",
    "        kappa = -np.log(b) / dt    \n",
    "        m = a / (1.0 - np.exp(-kappa * dt))\n",
    "        sigma = np.sqrt(var * 2.0 * kappa / (1.0 - np.exp(-2.0 * kappa * dt)))\n",
    "        sigmaeq = np.sqrt(var / (1.0 - np.exp(-2.0 * kappa * dt)));\n",
    "        return kappa, m, sigma, sigmaeq\n",
    "    else:\n",
    "        return -1.0,0,0,0\n",
    "\n",
    "\n",
    "def sscore(m, sigmaeq):\n",
    "    if sigmaeq != 0:\n",
    "        return -m/sigmaeq\n",
    "    elif m>0:\n",
    "        return 10000000\n",
    "    else:\n",
    "        return -10000000\n",
    "\n",
    "\n",
    "def plot_graph(pnl, ticker1, ticker2):\n",
    "    pnl_array = np.asarray(pnl)\n",
    "    plt.plot(pnl_array)\n",
    "    plt.title('PnL for {} vs {}'.format(ticker1, ticker2))\n",
    "    plt.show()\n",
    "    return None\n",
    "\n",
    "\n",
    "### Definition of the class\n",
    "class Ave_Lee(object):\n",
    "    def __init__(self, session, date, tickers, start_time, end_time, pnl, buy_shares1, buy_dollars, \n",
    "                 sell_shares1, sell_dollars, buy_shares2, sell_shares2, rho, c, interval, training_size, trades):\n",
    "        self.session = session\n",
    "        self.date = date\n",
    "        self.tickers = tickers\n",
    "        self.ticker1 = self.tickers[0]\n",
    "        self.ticker2 = self.tickers[1]\n",
    "        self.start_time = start_time\n",
    "        self.end_time = end_time\n",
    "        self.halt_trading = string_to_micro('5m') # close position 5 minutes before trading ends\n",
    "        self.interval = string_to_micro(interval)\n",
    "        self.interval_pnl = string_to_micro('1s')\n",
    "        self.state = 'NULL'\n",
    "        \n",
    "        # variables for BUY or SELL\n",
    "        self.side1 = 0\n",
    "        self.side2 = 0\n",
    "        \n",
    "        # variables for order size\n",
    "        self.order_size1 = 100\n",
    "        self.order_size2 = 1\n",
    "        self.buy_size = 10000 * 10**6 # how much we can buy for in each trade\n",
    "        \n",
    "        # variables to keep track of total shares bought/sold and the corresponding amount of money\n",
    "        self.buy_shares1 = buy_shares1\n",
    "        self.buy_dollars = buy_dollars\n",
    "        self.sell_shares1 = sell_shares1\n",
    "        self.sell_dollars = sell_dollars\n",
    "        self.buy_shares2 = buy_shares2\n",
    "        self.sell_shares2 = sell_shares2\n",
    "        \n",
    "        # variables to keep track of how many positions we have opened and closed respectively\n",
    "        self.trades = trades\n",
    "        \n",
    "        # variables used for the fitOU, when to open/close a position and how far we look back\n",
    "        self.dt = 1\n",
    "        self.rho = rho #urgency parameter for when to open/close\n",
    "        self.c = c #measure of transaction costs\n",
    "        self.training_size = training_size\n",
    "        \n",
    "        # start timer/ call the start_callback function\n",
    "        self.session.add_timer(self.start_time, self.start_callback)\n",
    "        \n",
    "        # list to store pnl every time we update it\n",
    "        self.pnl = pnl\n",
    "        self.pnl2 = []\n",
    "        \n",
    "        # dictionary to store time, midprices and the returns each timestep\n",
    "        self.results = {'time': []}\n",
    "        for ticker in self.tickers:\n",
    "            self.results[ticker] = []\n",
    "            self.results['return {}'.format(ticker)] = []\n",
    "        \n",
    "        # subscribe to the tickers of interest, and set the first timer\n",
    "        for ticker in self.tickers:\n",
    "            self.session.subscribe_ticker_all_feeds(ticker)\n",
    "    \n",
    "    \n",
    "    def start_callback(self, time):\n",
    "        for ticker in self.tickers:\n",
    "            self.session.subscribe_event(ticker, ORDER_EVENTS, self.event_callback)\n",
    "        self.session.add_timer(time, self.timer_callback1)\n",
    "        self.session.add_timer(time, self.timer_callback2)\n",
    "        \n",
    "    \n",
    "    def event_callback(self, ticker, event_params):\n",
    "        # call the execution manager whenever we have an execution\n",
    "        self.process_executions(event_params)\n",
    "        \n",
    "        \n",
    "    def timer_callback1(self, time):\n",
    "        # update pnl every second to see how it evolves over the day            \n",
    "        pnl = self.get_pnl()\n",
    "        self.pnl.append(pnl / 1000000.0)\n",
    "        self.pnl2.append(pnl / 1000000.0)\n",
    "        \n",
    "        if time < self.end_time:\n",
    "            self.session.add_timer(time + self.interval_pnl, self.timer_callback1)\n",
    "            \n",
    "    \n",
    "    def timer_callback2(self, time):\n",
    "        self.results['time'].append(micro_to_time(time))\n",
    "        \n",
    "        # append the midprices\n",
    "        self.results[self.ticker1].append(self.get_midmarket(self.ticker1) / 1000000.0)\n",
    "        self.results[self.ticker2].append(self.get_midmarket(self.ticker2) / 1000000.0)\n",
    "        \n",
    "        # start calculating returns after 1 second\n",
    "        if time > self.start_time + 10**6:\n",
    "            self.results['return {}'.format(self.ticker1)].append(np.float(returns(self.results[self.ticker1][-2:])))\n",
    "            self.results['return {}'.format(self.ticker2)].append(np.float(returns(self.results[self.ticker2][-2:])))\n",
    "        \n",
    "        # get the best bid and offer, compute the midmarket\n",
    "        bid1, ask1 = self.session.get_inside_market(self.ticker1)\n",
    "        bid2, ask2 = self.session.get_inside_market(self.ticker2)\n",
    "        # start collecting signals after training_size * 1 second\n",
    "        if time > self.start_time + self.halt_trading + self.training_size * 10**6:\n",
    "            # collect the last training_size of returns\n",
    "            returns1 = self.results['return {}'.format(self.ticker1)][-self.training_size:]\n",
    "            returns2 = self.results['return {}'.format(self.ticker2)][-self.training_size:]\n",
    "            # regress the returns and fit the residuals, calculate the s-score\n",
    "            residuals, a,b = regress(returns1,returns2)\n",
    "            kappa, m, sigma, sigmaeq = fitOU(residuals, self.training_size)\n",
    "            s = sscore(m, sigmaeq)\n",
    "            # find current net position (=0: neutral, <0: we are short asset 1, >0: we are long asset 1)\n",
    "            pos = self.buy_shares1 - self.sell_shares1            \n",
    "            # feature to check if we have orders at the market before we open a position\n",
    "            orders = self.session.get_all_orders()\n",
    "        \n",
    "            residual = residuals[-1]\n",
    "            \n",
    "            if not orders and b > 0 and kappa > 0 and sigma > 0 and time < self.end_time - self.halt_trading:\n",
    "                if pos == 0:\n",
    "                    if residual < long_open(self.rho,kappa,m,sigma, self.c):\n",
    "                        self.side1 = BUY\n",
    "                        self.side2 = SELL\n",
    "                        price1 = ask1['price']# - self.tick_size\n",
    "                        price2 = bid2['price']# + self.tick_size\n",
    "                        # make the portfolio self financing by making sure we sell for as much as we buy\n",
    "                        self.order_size1 = self.buy_size // price1\n",
    "                        self.order_size2 = int(b * self.order_size1)\n",
    "                        self.session.add_order(self.ticker1, self.side1, self.order_size1, price1, exchange=EXCH_INET)\n",
    "                        self.session.add_order(self.ticker2, self.side2, self.order_size2, price2, exchange=EXCH_INET)\n",
    "                        self.trades += 1                   \n",
    "                    elif residual > short_open(self.rho,kappa,m,sigma, self.c):\n",
    "                        self.side1 = SELL\n",
    "                        self.side2 = BUY\n",
    "                        price1 = bid1['price']# + self.tick_size\n",
    "                        price2 = ask2['price']# - self.tick_size\n",
    "                        # make the portfolio self financing by making sure we buy for as much as we sell\n",
    "                        self.order_size1 = self.buy_size // price1\n",
    "                        self.order_size2 = int(b * self.order_size1)\n",
    "                        self.session.add_order(self.ticker1, self.side1, self.order_size1, price1, exchange=EXCH_INET)\n",
    "                        self.session.add_order(self.ticker2, self.side2, self.order_size2, price2, exchange=EXCH_INET)\n",
    "                        self.trades += 1                    \n",
    "                elif pos < 0 and residual < short_close(self.rho,kappa,m,sigma, self.c):\n",
    "                    self.side1 = BUY\n",
    "                    self.side2 = SELL\n",
    "                    price1 = ask1['price']# - self.tick_size\n",
    "                    price2 = bid2['price']# + self.tick_size\n",
    "                    self.session.add_order(self.ticker1, self.side1, self.order_size1, price1, exchange=EXCH_INET)\n",
    "                    self.session.add_order(self.ticker2, self.side2, self.order_size2, price2, exchange=EXCH_INET)  \n",
    "                elif pos > 0 and residual > long_close(self.rho,kappa,m,sigma, self.c):\n",
    "                    self.side1 = SELL\n",
    "                    self.side2 = BUY\n",
    "                    price1 = bid1['price']# + self.tick_size\n",
    "                    price2 = ask2['price']# - self.tick_size\n",
    "                    self.session.add_order(self.ticker1, self.side1, self.order_size1, price1, exchange=EXCH_INET)\n",
    "                    self.session.add_order(self.ticker2, self.side2, self.order_size2, price2, exchange=EXCH_INET)\n",
    "        \n",
    "        if time >= self.end_time - self.halt_trading and pos != 0:\n",
    "            if pos < 0:\n",
    "                self.side1 = BUY\n",
    "                self.side2 = SELL\n",
    "                price1 = ask1['price']# - self.tick_size\n",
    "                price2 = bid2['price']# + self.tick_size\n",
    "                self.session.add_order(self.ticker1, self.side1, self.order_size1, price1, exchange=EXCH_INET)\n",
    "                self.session.add_order(self.ticker2, self.side2, self.order_size2, price2, exchange=EXCH_INET)\n",
    "                \n",
    "            elif pos > 0:\n",
    "                self.side1 = SELL\n",
    "                self.side2 = BUY\n",
    "                price1 = bid1['price']# + self.tick_size\n",
    "                price2 = ask2['price']# - self.tick_size\n",
    "                self.session.add_order(self.ticker1, self.side1, self.order_size1, price1, exchange=EXCH_INET)\n",
    "                self.session.add_order(self.ticker2, self.side2, self.order_size2, price2, exchange=EXCH_INET)\n",
    "                \n",
    "            \n",
    "        # reset the timer unless we are done \n",
    "        if time < self.end_time:\n",
    "            self.session.add_timer(time + self.interval, self.timer_callback2)\n",
    "                \n",
    "            \n",
    "    def process_executions(self, evp):\n",
    "        # make sure that we only update if we have executed any orders\n",
    "        # when we want to add transaction costs we do it in this function\n",
    "        if 'executed_orders' in evp:\n",
    "            time = self.session.current_time()\n",
    "            for ex in evp['executed_orders']:\n",
    "                order = ex['order']\n",
    "                side = order['side']\n",
    "                ticker = order['ticker']\n",
    "                if ticker == self.ticker1:\n",
    "                    if side == 'B':\n",
    "                        self.buy_shares1 += ex['quantity_executed']\n",
    "                        #self.buy_dollars += ex['quantity_executed'] * ex['price_executed']\n",
    "                        # buy in midmarker to check if spread is \"eating\" profits\n",
    "                        self.buy_dollars += ex['quantity_executed'] * self.get_midmarket(ticker)\n",
    "                    else:\n",
    "                        self.sell_shares1 += ex['quantity_executed']\n",
    "                        #self.sell_dollars += ex['quantity_executed'] * ex['price_executed']\n",
    "                        # sell in midmarker to check if spread is \"eating\" profits\n",
    "                        self.sell_dollars += ex['quantity_executed'] * self.get_midmarket(ticker)\n",
    "                    pos = self.buy_shares1 - self.sell_shares1\n",
    "                elif ticker == self.ticker2:\n",
    "                    if side == 'B':\n",
    "                        self.buy_shares2 += ex['quantity_executed']\n",
    "                        #self.buy_dollars += ex['quantity_executed'] * ex['price_executed']\n",
    "                        # buy in midmarker to check if spread is \"eating\" profits\n",
    "                        self.buy_dollars += ex['quantity_executed'] * self.get_midmarket(ticker)\n",
    "                    else:\n",
    "                        self.sell_shares2 += ex['quantity_executed']\n",
    "                        #self.sell_dollars += ex['quantity_executed'] * ex['price_executed']\n",
    "                        # sell in midmarker to check if spread is \"eating\" profits\n",
    "                        self.sell_dollars += ex['quantity_executed'] * self.get_midmarket(ticker)\n",
    "                    pos = self.buy_shares2 - self.sell_shares2        \n",
    "                pnl = self.get_pnl()\n",
    "                \n",
    "                \n",
    "\n",
    "    def get_midmarket(self, ticker):\n",
    "        bid, ask = self.session.get_inside_market(ticker)\n",
    "        return (bid['price'] + ask['price']) / 2.0\n",
    "    \n",
    "    \n",
    "    def get_pnl(self):\n",
    "        # mark to the midmarket\n",
    "        mid1 = self.get_midmarket(self.ticker1)\n",
    "        mid2 = self.get_midmarket(self.ticker2)\n",
    "        pnl = self.sell_dollars - self.buy_dollars + (self.buy_shares1 - self.sell_shares1) * mid1 + (self.buy_shares2 - self.sell_shares2) * mid2\n",
    "        return pnl\n",
    "    \n",
    "    \n",
    "    def end(self):\n",
    "        plot_graph(self.pnl2, self.ticker1, self.ticker2)\n",
    "        return (self.pnl, self.buy_shares1, self.buy_dollars, self.sell_shares1, self.sell_dollars, self.buy_shares2, self.sell_shares2, self.trades)\n",
    "\n",
    "    \n",
    "\n",
    "# generalize the process of simulating the algorithm\n",
    "start_time = string_to_micro(\"9:30\")\n",
    "end_time = string_to_micro(\"16:00\")\n",
    "\n",
    "tickers1 = ['ORCL', 'AAPL', 'NVDA', 'TXN', 'AAPL']\n",
    "tickers2 = ['NVDA', 'ORCL', 'MSFT', 'AAPL', 'CSCO']\n",
    "\n",
    "# depending on what parameter is being tuned, option will correspond to the element in that parameters list\n",
    "interval = '5s'\n",
    "training_size = 100\n",
    "c = 0\n",
    "rho = 0.01\n",
    "\n",
    "\n",
    "for i in range(len(tickers1)):  \n",
    "    ticker1 = tickers1[i]\n",
    "    ticker2 = tickers2[i]\n",
    "        \n",
    "    print(str(ticker1) + ' vs ' + str(ticker2))\n",
    "    \n",
    "    # Reset parameters for every new pair\n",
    "    pnl = []           \n",
    "    buy_shares1 = 0\n",
    "    buy_dollars = 0\n",
    "    sell_shares1 = 0\n",
    "    sell_dollars = 0\n",
    "    buy_shares2 = 0\n",
    "    sell_shares2 = 0   \n",
    "    trades = 0\n",
    "        \n",
    "    dates = ['20170410', '20170411', '20170412', '20170413', '20170417', '20170418', '20170419', '20170420',\n",
    "                '20170421', '20170424'] # '20170425', '20170426', '20170427', '20170428'\n",
    "    for date in dates:\n",
    "        sim = Simulator(Ave_Lee)\n",
    "        (pnl, buy_shares1, buy_dollars, sell_shares1, sell_dollars, buy_shares2, sell_shares2, trades) = sim.run(date, [ticker1, ticker2], use_om=True, start_time=start_time, end_time=end_time, \n",
    "                    pnl=pnl, buy_shares1=buy_shares1, buy_dollars=buy_dollars, sell_shares1=sell_shares1,\n",
    "                    sell_dollars=sell_dollars, buy_shares2=buy_shares2, sell_shares2=sell_shares2,\n",
    "                    interval=interval, training_size=training_size, trades=trades, rho=rho, c=c)\n",
    "        \n",
    "    pnl_array = np.asarray(pnl)\n",
    "\n",
    "        \n",
    "    print('Last PnL: {}'.format(pnl[-1]))\n",
    "    if trades > 0:\n",
    "        print('Dollar per trade: {}'.format(pnl[-1] / (10000 * trades)))\n",
    "    else: print('No trades')\n",
    "    \n",
    "    plt.plot(pnl_array)\n",
    "    plt.title('PnL for {} vs {}'.format(ticker1, ticker2))\n",
    "    plt.show()\n",
    "            "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python2",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
